/*
 * DatabaseDemoHostApplication.java
 */
package org.sunspotworld.demo;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Vector;
import org.sunspotworld.manager.DTNManager;
import org.sunspotworld.executor.DataExecutor;
import org.sunspotworld.manager.WSNManager;

/**
 * This class is starting point of the Core-Engine Sender application
 * @author Laili Aidi - aidi@kth.se
 * Condroid Summer CSD 2011 - KTH
 */
public class DatabaseDemoHostApplication {

    private DataExecutor obDataExecutor;
    private DTNManager obDTNManager;
    private WSNManager obWSNManager;
    // Duration (in milliseconds) for which to collect samples.
    private static final int FILE_DURATION = 60000;
    //The PATH of this DTN FILE, THIS SHOULD BE DYNAMIC / SET BY USER */
    private String obStrOutgoingPath;
    /* The Receiver DTN Addr of the intended bundle, THIS SHOULD BE DYNAMIC / SET BY USER */
    private String obStrReceiver;
    //The Name of this WSN/DTN Gateway, THIS SHOULD BE DYNAMIC / SET BY USER */
    private String obStrGatewayAddr;
    //The Address of this WSN/DTN Gateway, THIS SHOULD BE DYNAMIC / SET BY USER */
    private String obStrGatewayName;
    private String obStrGatewayLocation;
    /* The dtnd path of the DTN node, THIS SHOULD BE DYNAMIC / SET BY USER */
    private String obStrDtndPath;
    /* The dtnd conf of the DTN node, THIS SHOULD BE DYNAMIC / SET BY USER */
    private String obStrDtnConfPath;

    /**
     * Start up the host application. 
     * ant host-run
     * @param args any command line arguments
     * @throws Exception  
     */
    public static void main(String[] args) throws Exception {
        DatabaseDemoHostApplication app = new DatabaseDemoHostApplication();
        String obStrGatewayName = "-";
        String obStrGatewayAddr = getMACAddress();
        String obStrGatewayLocation = "-";
        String obStrReceiver = "dtn://voyage2.bytewalla.com";
        String obStrOutgoingPath = "/root/core-engine/outgoing";
        String obStrDtndPath = "/root/dtn2/DTN2/daemon/dtnd";
        String obStrDtnConfPath = "/usr/voyagedtn/dtn.conf";

        /*
         * 
         * Optional parameters:
         * -p <incoming_path> -dtnd <dtnd_path> -conf <dtnconf_path>
         */

        int i = 0;
        while (i < args.length) {
            if (args[i].equals("-g")) {
                //The MAC ADDR of this WSN/DTN Gateway
                if (args[i + 1] != null && args[i + 1].length() > 0) {
                    obStrGatewayName = args[0];
                }
                i++;
            } else if (args[i].equals("-l")) {
                //The LOCATION of this WSN/DTN Gateway
                if (args[i + 1] != null && args[i + 1].length() > 0) {
                    obStrGatewayLocation = args[1];
                }
                i++;
            } else if (args[i].equals("-r")) {
                // The DTN RECEIVER ADDRESS of the intended bundle
                if (args[i + 1] != null && args[i + 1].length() > 0) {
                    obStrReceiver = args[2];
                }
                i++;
            } else if (args[i].equals("-p")) {
                // The OUTGOING FILE PATH for the bundle
                if (args[i + 1] != null && args[1 + 1].length() > 0) {
                    obStrOutgoingPath = args[3];
                }
                i++;
            } else if (args[i].equals("-dtnd")) {
                // The DTND PATH
                if (args[4] != null && args[4].length() > 0) {
                    obStrDtndPath = args[4];
                }
                i++;
            } else if (args[i].equals("-conf")) {
                // The DTN CONFIGURATION
                if (args[5] != null && args[5].length() > 0) {
                    obStrDtnConfPath = args[3];
                }
                i++;
            }
            i++;
        }
        app.runningControl(obStrGatewayAddr, obStrGatewayName, obStrGatewayLocation, obStrReceiver, obStrOutgoingPath, obStrDtndPath, obStrDtnConfPath);
    }

    /**
     * This method is used to get MAC address of the ALIX board
     * @return 
     */
    private static String getMACAddress() {
        try {
            InetAddress address = InetAddress.getLocalHost();
            NetworkInterface ni = NetworkInterface.getByInetAddress(address);
            if (ni != null) {

                byte[] mac = ni.getHardwareAddress();
                if (mac != null) {
                    StringBuilder sb = new StringBuilder(18);
                    for (byte b : mac) {
                        if (sb.length() > 0) {
                            sb.append(':');
                        }
                        sb.append(String.format("%02x", b));
                    }
                    return sb.toString().toUpperCase();
                }
            }
        } catch (UnknownHostException e) {
        } catch (SocketException e) {
        }
        return "";
    }
    distributerThread distributerThread;
    collectorThread collectorThread;
    boolean currentState;

    /**
     * Sender Engine Thread
     */
    private class distributerThread extends Thread {

        private long start;

        @Override
        public void run() {
            start = System.currentTimeMillis();
            String obStrFileSndName = "wsndtndata";
            while (!Thread.currentThread().isInterrupted()) {
                if (currentState) {
                    try {
                        //DTN SEND SCHEDULLING PART
                        if (System.currentTimeMillis() - start > FILE_DURATION) {
                            System.out.println("MESSAGE: Automatic distribution now");
                            schedullingDistribution();
                            //Restart the timer again
                            start = System.currentTimeMillis();
                        }
                        //DTN SEND CONTROLED PART
                        //Check if there is any data for controled distribution
                        obDTNManager.distributeData(obStrFileSndName);
                        Thread.sleep(1000);
                    } catch (Exception e) {
                    }
                }
            }


        }

        private void schedullingDistribution() {
            String obStrText = "";
            String[] obStrSensingData = new String[7];
            String[] obStrSpotData = new String[3];
            String[] obStrGatewayData = new String[3];
            String obStrFileSndName = "wsndtndata";// + System.currentTimeMillis();
            //Read sensing data from sensing table with UNSENT flag
            Vector obVectorData = obDataExecutor.readSensingTableUnsent();

            if (obVectorData.size() > 0) {
                Enumeration e = obVectorData.elements();
                for (int i = 0; i < obVectorData.size(); i++) {
                    //This is spot ID
                    obStrSensingData = (String[]) e.nextElement();
                    //Get the SPOT_ADDR and SPOT_LOCATION
                    obStrSpotData = obDataExecutor.getSpot(obStrSensingData[1]);
                    //Get the GATEWAY_ADDR, GATEWAY_NAME and GATEWAY_LOCATION
                    obStrGatewayData = obDataExecutor.getGateway(obStrSensingData[2]);

                    /*
                     * Make new line text
                     * The format is 
                     * 'GatewayAddr' 'GatewayName' 'GatewayLocation' 'SpotAddr' 'SpotLoc' 'timestap' 'light' 'temperature'
                     */
                    //Put the gateway information first
                    String obStrTemp = "\'" + obStrGatewayData[0] + "\' \'" + obStrGatewayData[1] + "\' \'" + obStrGatewayData[2] + "\' ";
                    //Put the spot information
                    obStrTemp += "\'" + obStrSpotData[1] + "\' \'" + obStrSpotData[2] + "\' ";
                    //Put the sensing information
                    obStrTemp += "\'" + obStrSensingData[3] + "\' \'" + obStrSensingData[4] + "\' \'" + obStrSensingData[5] + "\' \'" + obStrSensingData[6] + "\'\n";
                    //Add it to the file text
                    obStrText += obStrTemp;

                    //To make sure this operation is only be done when we finish to get all the data to the string text
                    if (i == obVectorData.size() - 1) {
                        //Then send it as the bundle
                        if (obDTNManager.distributeData(obStrFileSndName, obStrText)) {
                            //then update these sensing data to the sensing table as SENT flag
                            obDataExecutor.updateFlag(obStrSensingData[0], obStrSensingData[3], 1);
                        }
                    }
                }
            }
            //Restart the timer again
            start = System.currentTimeMillis();
        }
    }

    /**
     * Collection Engine Thread
     */
    private class collectorThread extends Thread {

        @Override
        public void run() {
            System.out.println("Ready to sampling for forever when it's started until it's stoped \n"
                    + "[Each * indicates one sample, ! implies"
                    + " radio timed out waiting for a sample]");

            while (!Thread.currentThread().isInterrupted()) {
                if (currentState) {
                    //Collect the data from WSN Motes and pool it to database
                    //--TEMP
                    obWSNManager.collectData();
                }
            }
        }
    }

    /**
     * This method is used as starting point of the Core-Engine
     * @param obStrGatewayAddr
     * @param obStrGatewayName
     * @param obStrGatewayLocation
     * @param obStrReceiver
     * @param obStrOutgoingPath
     * @param obStrDtndPath
     * @param obStrDtnConfPath 
     */
    private void runningControl(String obStrGatewayAddr, String obStrGatewayName, String obStrGatewayLocation, String obStrReceiver,
            String obStrOutgoingPath, String obStrDtndPath, String obStrDtnConfPath) {
        this.obStrGatewayAddr = obStrGatewayAddr;
        this.obStrGatewayName = obStrGatewayName;
        this.obStrGatewayLocation = obStrGatewayLocation;
        this.obStrReceiver = obStrReceiver;
        this.obStrOutgoingPath = obStrOutgoingPath;
        this.obStrDtndPath = obStrDtndPath;
        this.obStrDtnConfPath = obStrDtnConfPath;

        /* Declare Object for WSN distributerThread first */
        this.obDataExecutor = new DataExecutor();
        this.obWSNManager = new WSNManager(obDataExecutor, this.obStrGatewayAddr, this.obStrGatewayName, this.obStrGatewayLocation);
        this.obDTNManager = new DTNManager(this.obStrReceiver, obDataExecutor, this.obStrOutgoingPath, this.obStrDtndPath, this.obStrDtnConfPath);

        String line = null;
        String obStrText = "";
        int timespan = 1000; //check the file conten every 1 second
        Long start = System.currentTimeMillis();
        boolean isrunning = false;

        //Set up the database first, if it fails, the entire system should not continue running
        if (setUpDatabase()) {
            setUpWSNNManager();
            //Declare the Engine tread
            distributerThread = new distributerThread();
            collectorThread = new collectorThread();

            //Collect the data from DTN File and pool it to database
            collectorThread.start();
            distributerThread.start();
            System.out.println("\nSTARTING ENGINES [" + distributerThread.isAlive() + "," + collectorThread.isAlive() + "]");

            currentState = false;
            while (true) {
                try {
                    if (System.currentTimeMillis() - start > timespan) {
                        RandomAccessFile raf = new RandomAccessFile("/var/www/flag", "r");
                        //RandomAccessFile raf = new RandomAccessFile("flag", "r");
                        //check the flag now
                        obStrText = raf.readLine();
                        //close the file immedietly after it is not needed
                        raf.close();
                        if (obStrText != null && obStrText.length() > 0) {
                            obStrText.trim();
                            start = System.currentTimeMillis();
                            if (Integer.parseInt(obStrText) == 1 && !currentState) {
                                //Collect the data from WSN and Distribute to DTN File
                                startEngine();
                                //Means we have to START the DTN and WSN service
                                currentState = true;
                                System.out.println("\nSTARTING COLLECTING & DISTRIBUTING [" + currentState + "]");

                            } else if (Integer.parseInt(obStrText) == 0 && currentState) {
                                //Means we have to STOP the DTN and WSN service
                                currentState = false;
                                System.out.println("\nSTOPING COLLECTING & DISTRIBUTING [" + currentState + "]");

                            }
                        }
                    }
                } catch (FileNotFoundException ex) {
                    System.err.println("ERROR in runningControl 1: " + ex.getMessage() + " while reading running file.");
                } catch (IOException ie) {
                    System.err.println("ERROR in runningControl 2: " + ie.getMessage() + " while reading running file.");
                } catch (Exception e) {
                    System.err.println("ERROR in runningControl 2: " + e.getMessage() + " while reading running file.");
                }
            }
        }
        //tearDown();
    }

    /**
     * This method is used to start the Core-Engine
     */
    private void startEngine() {
        //Activate DTN Sender
        setUpDTNManager();
    }

    /**
     * This method is used to stop the Core-Engine
     */
    private void stopEngine() {
        try {
            while (distributerThread.isAlive()) {
                distributerThread.interrupt();
            }
        } catch (Exception e) {
            System.err.println("ERROR in stopEngine1 : " + e.getMessage() + " - Can not stop the engineDTNThread");
        }

        try {
            while (collectorThread.isAlive()) {
                collectorThread.interrupt();
            }
        } catch (Exception e) {
            System.err.println("ERROR in stopEngine2 : " + e.getMessage() + " - Can not stop the engineWSNThread");
        }

        System.out.println("\nSTOPING ENGINES [" + distributerThread.isAlive() + "," + collectorThread.isAlive() + "]");
    }

    /**
     * This method is used to set up the database
     * @return isSuccess
     */
    private boolean setUpDatabase() {
        if (obDataExecutor.connect()) {
            System.out.println("MESSAGE: Connecting to Database was successful");
            return true;
        }
        System.err.println("ERROR: Connecting to Database was fail");
        return false;
    }

    /**
     * This method is used to set up the WSN Manager
     * @return isSuccess
     */
    private boolean setUpWSNNManager() {
        if (obWSNManager.startWSN()) {
            System.out.println("MESSAGE: Starting WSN was successful");
            return true;
        }
        System.err.println("ERROR: Starting WSN was fail");
        return false;
    }

    /**
     * This method is used to set up the DTN Manager
     * @return isSuccess
     */
    private boolean setUpDTNManager() {
        String obStrReceiver = obDataExecutor.getReceiverName();
        if (obStrReceiver == null || obStrReceiver.length() <= 0) {
            obStrReceiver = this.obStrReceiver;
        }
        //Start the DTN Daemon
        if (obDTNManager.startDTN(obStrReceiver)) {
            System.out.println("MESSAGE: Starting DTN was successful");
            return true;
        }
        System.err.println("ERROR: Starting DTN was fail");
        return false;
    }

    /**
     * This method is used to teardown the application
     */
    public void tearDown() {
        try {
            stopEngine();
            obDataExecutor.disconnect();
            obWSNManager.stopWSN();
            //obDTNManager.stopDTN();
            System.exit(0);
        } catch (Exception e) {
        }
    }
}
